#ifndef _common_h
#define _common_h

#include <cstdio>
#include <cstdlib>
#include <string>
#include <ctime>

#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netdb.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <time.h>

#include <openssl/ssl.h>
#include <openssl/rsa.h>
#include <openssl/engine.h>
#include <openssl/pem.h>
#include <openssl/bio.h>

#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>

#define CA_LIST						"realkeys/root.pem"
#define DHFILE						"realkeys/dh1024.pem"

#define CARRIER_ENC_CN				"10.0.1.42"
#define CREDITCO_ENC_CN				"10.0.1.43"

#if DEBUG
#define CARRIER_ADDR				"localhost"
#define MERCHANT_ADDR				"localhost"
#define CREDITCO_ADDR				"localhost"
#else
#define CARRIER_ADDR				"10.0.1.42"
#define MERCHANT_ADDR				"10.0.1.10"
#define CREDITCO_ADDR				"10.0.1.43"
#endif

#define BUFSIZZ						1024

#define PUB_MOD_BITS				2048
#define PUB_EXP						RSA_F4
#define PADDING						RSA_PKCS1_OAEP_PADDING

#define CARRIER_PUB_KEY				"realkeys/carrierPubKey.pem"
#define CARRIER_PRIV_KEY			"realkeys/carrierPrivKey.pem"
#define CREDITCO_PUB_KEY			"realkeys/creditcoPubKey.pem"
#define CREDITCO_PRIV_KEY			"realkeys/creditcoPrivKey.pem"

#define MERCHANT_SERVER_PORT		4433
#define CARRIER_SERVER_PORT			4434
#define CREDITCO_SERVER_PORT		4435

#define ACK_STRING					"OK"
#define NACK_STRING					"NOK"

/* Order Info Product Entry Listing */
///note... this is much easier using structures,
//
#define OI_PROD_ENTRY_HEADER_LEN	1
#define OI_PROD_ENTRY_PROD_ID_LEN	2
#define OI_PROD_ENTRY_NAME_LEN		22
#define OI_PROD_ENTRY_PRICE_LEN		8
#define OI_PROD_ENTRY_NUM_LEN		6
#define OI_PROD_ENTRY_TERM_LEN		1

#define OI_MERCHANT_ID_LEN			10
#define OI_DATE_LEN					6
#define OI_TRANS_ID_LEN				8
#define OI_CREDITCO_CODE_LEN		2
#define OI_CARRIORCO_CODE_LEN		2
#define OI_TOTAL_CHARGED_LEN		8

#define PI_CARD_NUM_LEN				16
#define PI_CH_LAST_NAME_LEN			20
#define PI_CH_FIRST_NAME_LEN		20
#define PI_CH_MIDDLE_NAME_LEN		20
#define PI_EXP_DATE_LEN				4
#define PI_PAYMENT_AMT_LEN			8
#define PI_PAYMENT_DATE_LEN			6

#define SI_SHIPPING_ADDR_LEN		100
#define SI_ATTN_LAST_NAME_LEN		20
#define SI_ATTN_MIDDLE_NAME_LEN		20
#define SI_ATTN_FIRST_NAME_LEN		20
#define SI_CONTACT_NUM_LEN			16
#define SI_HANDLING_REQ_LEN			4
#define SI_CARRIER_CODE_LEN			2
#define SI_PUB_KEY_LEN				16

#define ORDER_TOKEN_LEN				24

#define CREDITCO_CODE				"CR"
#define	CARRIER_CODE				"SH"

#define TRANSACTIONS_PER_EXPERIMENT	100

struct ProductEntry {
	ProductEntry() {
		memset(this, 0, sizeof(ProductEntry));
	}

	unsigned char startByte;
	char id[OI_PROD_ENTRY_PROD_ID_LEN];
	char name[OI_PROD_ENTRY_NAME_LEN];
	char unitPrice[OI_PROD_ENTRY_PRICE_LEN];
	char quantity[OI_PROD_ENTRY_NUM_LEN];
	unsigned char endByte;
}__attribute__ ((__packed__));

struct OrderInfo {
	OrderInfo() {
		memset(this, 0, sizeof(OrderInfo));
	}

	char merchant_id[OI_MERCHANT_ID_LEN];
	char date[OI_DATE_LEN];
	char trans_id[OI_TRANS_ID_LEN];
	unsigned char totalItemsOrdered;
	char creditCardCoCode[OI_CREDITCO_CODE_LEN];
	char shippingCarrierCode[OI_CARRIORCO_CODE_LEN];
	char totalCharged[OI_TOTAL_CHARGED_LEN];
}__attribute__ ((__packed__));

struct PaymentInfo {
	PaymentInfo() {
		memset(this, 0, sizeof(PaymentInfo));
	}

	char ch_first_name[PI_CH_FIRST_NAME_LEN];
	char ch_mid_name[PI_CH_MIDDLE_NAME_LEN];
	char ch_last_name[PI_CH_LAST_NAME_LEN];
	char card_num[PI_CARD_NUM_LEN];
	char exp_date[PI_EXP_DATE_LEN];
	char payment_amount[PI_PAYMENT_AMT_LEN];
	char payment_date[PI_PAYMENT_DATE_LEN];
	char order_token[ORDER_TOKEN_LEN];
	unsigned char message_digest[EVP_MAX_MD_SIZE];
}__attribute__ ((__packed__));

struct ShippingInfo {
	ShippingInfo() {
		memset(this, 0, sizeof(ShippingInfo));
	}

	char attn_first_name[SI_ATTN_FIRST_NAME_LEN];
	char attn_mid_name[SI_ATTN_MIDDLE_NAME_LEN];
	char attn_last_name[SI_ATTN_LAST_NAME_LEN];
	char shipping_addr[SI_SHIPPING_ADDR_LEN];
	char contact_num[SI_CONTACT_NUM_LEN];
	char handling_req[SI_HANDLING_REQ_LEN];
	char carrier_code[SI_CARRIER_CODE_LEN];
	char pub_key[SI_PUB_KEY_LEN];
	char order_token[ORDER_TOKEN_LEN];
	unsigned char message_digest[EVP_MAX_MD_SIZE];
}__attribute__ ((__packed__));

extern BIO *bio_err;
int berr_exit(const char *string);
int err_exit(const char *string);

SSL_CTX *initialize_ctx(const char *keyfile, const char *password);
void destroy_ctx(SSL_CTX *ctx);

/* helper read function */
int SSL_read_until_full(SSL* ssl, void* buf, int buf_size);

/* utility function to generate RSA keys */
bool generate_rsa_keys(const char* rsa_pubkey_fname, const char* rsa_privkey_fname);

/* concatenates ShippingInfo fields into a single buffer for passing to sha1_digest */
unsigned char* prep_for_digest(const struct ShippingInfo& ship_info);

/* concatenates PaymentInfo fields into a single buffer for passing to sha1_digest */
unsigned char* prep_for_digest(const struct PaymentInfo& pay_info);

/* generates 160-bit sha1 hash digest */
unsigned char* sha1_digest(unsigned char* buf, unsigned int buf_len, unsigned int* hash_bytes);

bool VerifyCertificate(X509* certToVerify, const char* commonName);

void timespec_diff(struct timespec* start, struct timespec* end, struct timespec* diff);

#ifndef ALLOW_OLD_VERSIONS
#if (OPENSSL_VERSION_NUMBER < 0x00905100L)
#error "Must use OpenSSL 0.9.6 or later"
#endif
#endif

#endif

